//
// WorldParser.cpp for  in /home/herpec_j/Dropbox/Projets/raytracer-2016/Sources/Parsing/
//
// Made by Jonathan
// Login   <ledey_l@epitech.net>
//
// Started on  Fri Sep 20 11:28:10 2013 Jonathan
// Last update Thu Sep 26 00:19:50 2013 Jonathan
//

#include	"Parsing/WorldParser.hpp"

/*
**
** BNF for a scene
**
** Scene ::= [Identifiant Parameters]*;
**
** Identifiant ::= '[' id ']';
**
** Parameters ::= '{' [Datas]* '}';
**
** Datas ::= id '=' DataValue;
**
** DataValue ::= -> ['\n' | ','];
**
*/

namespace	Parsing
{
  WorldParser::WorldParser(ProducterStream &rStream) : ConsumerParser(rStream)
  {
    return;
  }

  bool		WorldParser::Identifiant(std::string &rErrorMessage)
  {
    int		check = 0;

    SaveContext();
    if (ReadChar('[') && ++check && BeginCapture("id") && ReadIdentifier() && ++check
	&& EndCapture("id", mCurrentIdentifiant) && ReadChar(']'))
      {
	RestoreContext(false);
	return (true);
      }
    else
      {
	switch (check)
	  {
	  case 0:
	    rErrorMessage = "Parse Error: Expected a '[' followed by a block identifier and by a ']'";
	    break;
	  case 1:
	    rErrorMessage = "Parse Error: Expected a correct block identifier after '['";
	    break;
	  default:
	    rErrorMessage = "Parse Error: Expected a ']' after " + mCurrentIdentifiant + " block identifier";
	    break;
	  }
	RestoreContext(true);
	return (false);
      }
  }

  bool		WorldParser::DataValue(void)
  {
    std::list<char>	char_list;

    char_list.push_back(';');
    char_list.push_back('}');
    char_list.push_back('\n');
    return (ReadUntil(char_list));
  }

  bool		WorldParser::Datas(std::string &rErrorMessage)
  {
    int		check = 0;
    std::string	key;
    std::string	value;

    SaveContext();
    if (BeginCapture("key") && ReadIdentifier() && ++check && EndCapture("key", key)
	&& ReadChar(':') && ++check && BeginCapture("value")
	&& DataValue() && ++check && EndCapture("value", value))
      {
	ZERO_OR_MANY(ReadChar(';'));
	RestoreContext(false);
	return (true);
      }
    else
      {
	switch (check)
	  {
	  case 0:
	    rErrorMessage = "Parse Error: Expected a correct key identifier in `" + mCurrentIdentifiant + "' block";
	    break;
	  case 1:
	    rErrorMessage = "Parse Error: Expected a `:' after `" + key + "' key";
	    break;
	  default:
	    rErrorMessage = "Parse Error: Expected a value after a `:' in `" + mCurrentIdentifiant + "' block";
	    break;
	  }
	RestoreContext(true);
	return (false);
      }
  }

  bool		WorldParser::Parameters(std::string &rErrorMessage)
  {
    std::string	error_message;
    int		check = 0;

    SaveContext();
    if (ReadChar('{'))
      {
	ZERO_OR_MANY(Datas(error_message) && ++check);
	if (check == 0)
	  {
	    rErrorMessage = error_message;
	  }
	else if (ReadChar('}'))
	  {
	    RestoreContext(false);
	    return (true);
	  }
	else
	  {
	    rErrorMessage = "Parse Error: Expected a `}' at the end of " + mCurrentIdentifiant + " block";
	  }
      }
    else
      {
	rErrorMessage = "Parse Error: Expected a `{' followed by some keys and values and by a `}' after " + mCurrentIdentifiant + " block declaration";
      }
    RestoreContext(true);
    return (false);
  }

  bool		WorldParser::Parse(void)
  {
    int		check = 0;
    std::string	error_message;

    ZERO_OR_MANY(Identifiant(error_message) && ++check && Parameters(error_message) && ++check);
    if (check == 0 || check % 2)
      {
	std::cerr << error_message << std::endl;
	return (false);
      }
    if (!ReadEOF())
      {
	std::cerr << "Parse Error: Expected another block or EOF after `" + mCurrentIdentifiant + "' block" << std::endl;
	return (false);
      }
    return (true);
  }

  WorldParser::~WorldParser(void)
  {
    return;
  }
};
